import {gecko, ie, ie_version, mobile, webkit} from "../util/browser.js"
import {elt, eltP} from "../util/dom.js"
import {scrollerGap} from "../util/misc.js"
import {getGutters, renderGutters} from "./gutters.js"

// The display handles the DOM integration, both for input reading
// and content drawing. It holds references to DOM nodes and
// display-related state.

export function Display(place, doc, input, options) {
    let d = this
    this.input = input

    // Covers bottom-right square when both scrollbars are present.
    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
    d.scrollbarFiller.setAttribute("cm-not-content", "true")
    // Covers bottom of gutter when coverGutterNextToScrollbar is on
    // and h scrollbar is present.
    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
    d.gutterFiller.setAttribute("cm-not-content", "true")
    // Will contain the actual code, positioned to cover the viewport.
    d.lineDiv = eltP("div", null, "CodeMirror-code")
    // Elements are added to these to represent selection and cursors.
    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
    d.cursorDiv = elt("div", null, "CodeMirror-cursors")
    // A visibility: hidden element used to find the size of things.
    d.measure = elt("div", null, "CodeMirror-measure")
    // When lines outside of the viewport are measured, they are drawn in this.
    d.lineMeasure = elt("div", null, "CodeMirror-measure")
    // Wraps everything that needs to exist inside the vertically-padded coordinate system
    d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
        null, "position: relative; outline: none")
    let lines = eltP("div", [d.lineSpace], "CodeMirror-lines")
    // Moved around its parent to cover visible view.
    d.mover = elt("div", [lines], null, "position: relative")
    // Set to the height of the document, allowing scrolling.
    d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
    d.sizerWidth = null
    // Behavior of elts with overflow: auto and padding is
    // inconsistent across browsers. This is used to ensure the
    // scrollable area is big enough.
    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
    // Will contain the gutters, if any.
    d.gutters = elt("div", null, "CodeMirror-gutters")
    d.lineGutter = null
    // Actual scrollable element.
    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
    d.scroller.setAttribute("tabIndex", "-1")
    // The element in which the editor lives.
    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")

    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
    if (ie && ie_version < 8) {
        d.gutters.style.zIndex = -1;
        d.scroller.style.paddingRight = 0
    }
    if (!webkit && !(gecko && mobile)) d.scroller.draggable = true

    if (place) {
        if (place.appendChild) place.appendChild(d.wrapper)
        else place(d.wrapper)
    }

    // Current rendered range (may be bigger than the view window).
    d.viewFrom = d.viewTo = doc.first
    d.reportedViewFrom = d.reportedViewTo = doc.first
    // Information about the rendered lines.
    d.view = []
    d.renderedView = null
    // Holds info about a single rendered line when it was rendered
    // for measurement, while not in view.
    d.externalMeasured = null
    // Empty space (in pixels) above the view
    d.viewOffset = 0
    d.lastWrapHeight = d.lastWrapWidth = 0
    d.updateLineNumbers = null

    d.nativeBarWidth = d.barHeight = d.barWidth = 0
    d.scrollbarsClipped = false

    // Used to only resize the line number gutter when necessary (when
    // the amount of lines crosses a boundary that makes its width change)
    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
    // Set to true when a non-horizontal-scrolling line widget is
    // added. As an optimization, line widget aligning is skipped when
    // this is false.
    d.alignWidgets = false

    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null

    // Tracks the maximum line length so that the horizontal scrollbar
    // can be kept static when scrolling.
    d.maxLine = null
    d.maxLineLength = 0
    d.maxLineChanged = false

    // Used for measuring wheel scrolling granularity
    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null

    // True when shift is held down.
    d.shift = false

    // Used to track whether anything happened since the context menu
    // was opened.
    d.selForContextMenu = null

    d.activeTouch = null

    d.gutterSpecs = getGutters(options.gutters, options.lineNumbers)
    renderGutters(d)

    input.init(d)
}
